package fr.vahren.sriksal.generation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Eroder {

	private static final int HEIGHT_MAP_TURB_FACTOR = 8;

	private static final int HEIGHT_MAP_NOISE_FACTOR = 8;

	private static final int TEMPERATURE_MAP_TURB_FACTOR = 5;

	private static final int TEMPERATURE_MAP_NOISE_FACTOR = 5;

	private static final int HUMIDITY_MAP_TURB_FACTOR = 5;

	private static final int HUMIDITY_MAP_NOISE_FACTOR = 5;

	private static final int maxDrops = 500;

	private ArrayList<Drop> drops;

	private DropStrategy strategy;

	// Eroder configuration
	private int iterations = 100000;
	private int dropCapacity = 1;
	private int dropFreq = 100;
	private int maxAlt = 255;

	private RiverGenerationStrategy riverGenerationStrategy;

	private Map<String, int[][]> maps;

	private BiomeGenerationStrategy biomeGenerationSrategy;

	// biomes
	public static final int WOOD = 1;
	public static final int SAND = 2;

	public enum StrategyMode {
		RAIN, RIVER
	};

	public enum RainGenerationMode {
		RIVERSPAWNER
	};

	public enum BiomeGeneationMode {
		DEFAULT
	};

	public Eroder(StrategyMode erosionMode, RainGenerationMode rainMode,
			BiomeGeneationMode biomeMode) {
		maps = new HashMap<String, int[][]>();
		drops = new ArrayList<Drop>();
		switch (erosionMode) {
		case RAIN:
			strategy = new RainDropStrategy(dropCapacity, dropFreq, maxAlt,
					drops);
			break;

		case RIVER:
			strategy = new RiverDropStrategy(dropCapacity, dropFreq, maxAlt,
					drops);
			break;

		default:
			strategy = new RainDropStrategy(dropCapacity, dropFreq, maxAlt,
					drops);
			break;
		}

		switch (rainMode) {
		case RIVERSPAWNER:
			riverGenerationStrategy = new RiverSpawnerStrategy();
			break;

		default:
			riverGenerationStrategy = new RiverSpawnerStrategy();
			break;
		}

		switch (biomeMode) {
		case DEFAULT:
			biomeGenerationSrategy = new DefaultBiomeGenerationStrategy();
			break;

		default:
			biomeGenerationSrategy = new DefaultBiomeGenerationStrategy();
			break;
		}

	}

	public void setMap(String name, int[][] map) {
		maps.put(name, map);
	}

	public void erode(int height, int width) {
		// generate height map and store it
		int[][] map = Generator.generateHeightMap(height, width,
				HEIGHT_MAP_NOISE_FACTOR, HEIGHT_MAP_TURB_FACTOR);
		setMap("height", map);
		strategy.init(map);

		DrawingUtils.getInstance().drawMap(map, strategy.getTerrainMap(),
				drops, 1);
		System.out.print(">");
		for (int i = 0; i < iterations; i++) {
			// Create drops
			if (drops.size() < maxDrops) {
				strategy.spawnDrops(map);
			}

			// Iterate on each drop
			for (int d = 0; d < drops.size(); d++) {
				// Calculate next position
				Position nextPosition = strategy.getNextPosition(drops.get(d),
						map);
				if (nextPosition != null) {
					// Fill the drop
					strategy.fillDrop(drops.get(d), nextPosition, map);
					// Move or evaporate
					strategy.move(drops.get(d), nextPosition, map);
				}// Else the drop has evaporated!
			}
			if (i % (iterations / 100) == 0) {
				DrawingUtils.getInstance().drawMap(map,
						strategy.getTerrainMap(), drops, 1);
				if (i % (iterations / 10) == 0) {
					System.out.print("#");
				} else {
					System.out.print("=");
				}
			}
		}
		DrawingUtils.getInstance().drawMap(map, strategy.getTerrainMap(),
				drops, 1);

		// store terrain map and density map
		maps.put("terrain", strategy.getTerrainMap());

		System.out.print("<");
	}

	public void generateRivers() {
		int[][] map = maps.get("height");
		int[][] terrainMap = maps.get("terrain");
		riverGenerationStrategy.init(map);

		riverGenerationStrategy.generateRivers(map, terrainMap);
		riverGenerationStrategy.fillSeas(map);
		// store water map
		setMap("water", riverGenerationStrategy.getWaterMap());
	}

	public void generateBiomes() {
		int[][] map = maps.get("height");
		int[][] temperatureMap = Generator.generateTemperatureMap(map.length,
				map[0].length, TEMPERATURE_MAP_NOISE_FACTOR,
				TEMPERATURE_MAP_TURB_FACTOR);
		maps.put("temperature", temperatureMap);
		int[][] humidityMap = Generator.generateHumidityMap(map.length,
				map[0].length, HUMIDITY_MAP_NOISE_FACTOR,
				HUMIDITY_MAP_TURB_FACTOR);
		maps.put("humidity", temperatureMap);
		int[][] waterMap = maps.get("water");

		// biomes
		int[][] biomeMap = new int[map.length][map[0].length];
		biomeGenerationSrategy.generateBiome(map, temperatureMap, humidityMap,
				waterMap, maps.get("terrain"), biomeMap);

		// store he biome map!
		setMap("biome", biomeMap);

		DrawingUtils.getInstance().drawAll(waterMap, map, maps.get("terrain"),
				biomeMap);
	}

	public int[][] getMap(String name) {
		return maps.get(name);
	}

}
